package es.mahulo.battleship.service;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import es.mahulo.battleship.api.dao.GameConfigDao;
import es.mahulo.battleship.api.dao.GameDao;
import es.mahulo.battleship.api.dao.PlayerDao;
import es.mahulo.battleship.api.dao.UserDao;
import es.mahulo.battleship.api.service.Config;
import es.mahulo.battleship.api.service.GameService;
import es.mahulo.battleship.model.Cell;
import es.mahulo.battleship.model.Game;
import es.mahulo.battleship.model.GameConfig;
import es.mahulo.battleship.model.GameStatus;
import es.mahulo.battleship.model.Player;
import es.mahulo.battleship.model.Ship;
import es.mahulo.battleship.model.Shot;
import es.mahulo.battleship.model.User;

@Service
public class GameServiceImpl implements GameService {
	
	Logger  logger = Logger.getLogger(GameServiceImpl.class.getName());
	

	
	private Config configService;
	private PlayerDao playerDao;
	private GameDao gameDao;
	private UserDao userDao;
	private GameConfigDao gameConfigDao;
	
	
	
	@Autowired
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	@Autowired
	public void setGameConfigDao(GameConfigDao gameConfigDao) {
		this.gameConfigDao = gameConfigDao;
	}


	@Autowired
	public void setConfigService(Config configService) {
		this.configService = configService;
	}
	
	@Autowired
	public void setPlayerDao(PlayerDao playerDao) {
		this.playerDao = playerDao;
	}

	@Autowired
	public void setGameDao(GameDao gameDao) {
		this.gameDao = gameDao;
	}

	public GameServiceImpl() {
	}
	
	@Override
	@Transactional
	public List<Game> getAllFromUser(String userId) {
		
		User user = userDao.findByName(userId);
		
		
		logger.debug("user found: " + user);
		
		List<Game> games = new ArrayList<Game>();

		for (Player player :user.getPlayers() ) {
			Game game = player.getGame();
			if (game.getStatus()!=GameStatus.Wind1 || 
					game.getStatus()!=GameStatus.Wind2 ||
					game.getStatus()!=GameStatus.Draw ) {
				games.add(game);
			}
		}
		
 
		return games;
	}

	
	
	@Override
	@Transactional
	public Game startUp(String userId) throws Exception {
		logger.info("createGame " + userId);
		
		User user = userDao.findByName(userId);
		
		logger.debug("user found: " + user);

		Game game = new Game();

		GameConfig gameConfig = gameConfigDao.find(1L); //TODO DEFINE A TABLE DEFAULT CONFIGURATION
		
		List<Player> players = new ArrayList<Player>();
		
		Player player = new Player();
		player.setUser(user);
		player.setShips(new ArrayList<Ship>());
		player.setShots(new ArrayList<Shot>());
		player.setGame(game);
		
		players.add(player); 

		game.setGameConfig(gameConfig);
		game.setStatus(GameStatus.StartUp);
		game.setPlayers(players);
		
		gameDao.create(game);
		
		logger.debug("game save: " + game);
		
		return game;
	}
	
	@Override
	@Transactional
	public Game joinPlayer(Long gameId, String userId)  throws Exception {
		
		Game game = gameDao.find(gameId);
		
		if (game.getStatus() == GameStatus.StartUp) {
			User user = userDao.findByName(userId);
			
			Player player = new Player();
			player.setUser(user);
			player.setShips(new ArrayList<Ship>());
			player.setShots(new ArrayList<Shot>());
			player.setGame(game);

			List<Player> players = game.getPlayers();
			players.add(player);
			
			game.setStatus(GameStatus.Configuring);
			
			gameDao.update(game);
			
			return game;
		} else {
			throw new Exception("Wrong State");
		}
	}


	
	@Override
	@Transactional(rollbackFor=Exception.class)
	public void addShip(Long gameId, String userId, List<Ship> ships) throws Exception {
		logger.info("Add Ship " + userId + " "  + ships);
		
		Game game = gameDao.find(gameId);
		
		Player player = foundPlayer(game.getPlayers(),userId);
		
		logger.info("Player found " + player);
		
		if (  game.getStatus() == GameStatus.Configuring )  {
			
			if (configService.isComplete(game.getGameConfig(),player) ) {
				logger.info("Configuration complete, not possible to add more ships.");
				
				throw new Exception("Configuration complete, not possible to add more ships.");
			}

			
			for (Ship ship : ships) {
				configService.isValid (game, player,ship); 
				player.getShips().add(ship);
				ship.setPlayer(player);
				for (Cell cell : ship.getCells()) {
					cell.setHit(false);
					cell.setShip(ship);
				}
			}
			
			if (!configService.isComplete(game.getGameConfig(),player) ) {
				logger.info("Ship configuration incorrect.");
				
				throw new Exception("Ship configuration incorrect.");
			}
			
			playerDao.create(player);
			
			List<Player> players = game.getPlayers();
			if (players.size() == 2) {
				if (configService.isComplete(game.getGameConfig(),players.get(0)) && 
						configService.isComplete(game.getGameConfig(),players.get(1))) {
					game.setStatus(GameStatus.Round1);
				}
			}
					
		} else {
			logger.info("Wrong State");
			
			throw new Exception("Wrong State");
		}
		
	}
	

	@Override
	public Boolean shot(Long gameId, String userId, Shot shot) throws Exception {
		logger.info("shot " + userId + " " + shot);
		
		Game game = gameDao.find(gameId);
		logger.debug("game " + game);
	
		Player player = foundPlayer(game.getPlayers(),userId);
		logger.debug("found " + player);
		
		
		Boolean isHit = false;		
		switch (game.getStatus()) {
			case Round1:
				logger.debug("Round 1 ");
				
				if (game.getPlayers().get(1) != null && 
						game.getPlayers().get(1).equals(player)) {
					  
					isHit = shot(game.getPlayers().get(1),shot);
					
					logger.debug("isHit "+ isHit);
					
					if (isAllShipDestroyed(game.getPlayers().get(1)) ) {  
						game.setStatus(GameStatus.Wind1);
						
						logger.debug("All Ship Destroyed ");
						
					} else 
						game.setStatus(GameStatus.Round2);
					
				} else {
					throw new Exception("Wrong state");
				}
				break;
			case Round2:
				logger.debug("Round 2 ");
				
				if (game.getPlayers().get(0) != null && 
					game.getPlayers().get(0).equals(player)) { 
				
					isHit = shot(game.getPlayers().get(0),shot);
					
					logger.debug("isHit "+ isHit);
					
					if (isAllShipDestroyed(game.getPlayers().get(0)) ) {  
						game.setStatus(GameStatus.Wind2);
						
						logger.debug("All Ship Destroyed ");
						
					} else
						game.setStatus(GameStatus.Round1);
				} else 
					throw new Exception("Wrong state");	
				break;
			case Wind1:
				logger.debug("Wind 1 ");
				
				if (game.getPlayers().get(0) != null && 
						game.getPlayers().get(0).equals(player)) { 
					
					isHit = shot(game.getPlayers().get(0),shot);
					
					logger.debug("isHit "+ isHit);
					
					if (isAllShipDestroyed(game.getPlayers().get(0)) )  {
						game.setStatus(GameStatus.Draw);
						
						logger.debug("All Ship Destroyed ");
					} else {
						game.setStatus(GameStatus.Wind1);
					}
					
				} else 
					throw new Exception("Wrong state");	
				break;
				
			default:
				throw new Exception("Wrong state");
		}
		
		gameDao.update(game);
		
		return isHit;
	}
	

	public Boolean shot(Player player,Shot shot) {
		for (Ship ship : player.getShips() )
			for (Cell cell: ship.getCells() ) {
				if (cell.getX()==shot.getX() && cell.getY()==shot.getY()) {
					cell.setHit(true);
					return true;
				}
			}
		return false;
	}

	private Player foundPlayer(List<Player> players,String userId) throws Exception {
		logger.info("found Player " + players + " " + userId);
		Player player = null;
		for (Player aPlayer :players) {
			if (aPlayer.getUser().getName().equals(userId)) {
				player = aPlayer;
				break;
			}
		}
		if (player == null) throw new Exception("Player not found");
		
		return player;
	}
	
	public Boolean isAllShipDestroyed(Player player)  {
		for (Ship ship : player.getShips() )
			for (Cell cell: ship.getCells() ) {
				if (!cell.getHit()) {
					return false;
				}
			}
		return true;
	}
}
